package com.example.base_model.utils

import androidx.lifecycle.LifecycleCoroutineScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch

/**
 * @Author jiangKunKun
 * @CreateDate 2024/8/13
 * @Description
 *  一个基于 Kotlin Flow 实现的事件总线。
 *  它允许应用程序的不同组件之间进行解耦的通信。
 *  特点：
 *  - 使用 SharedFlow 进行事件的多播。
 *  - 支持粘性事件（通过调整 replay 参数，当前默认非粘性）。
 *  - 事件的订阅与 Android 组件的生命周期绑定，防止内存泄漏。
 *  - 线程安全。
 * @SpittingGrooves
 */

object FlowEventBus {
    private val eventBusScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
    /**
     * 存储普通（非粘性）事件流的 Map。
     * Key: 事件的唯一标识符 (String)。
     * Value: 与该 Key 关联的 MutableSharedFlow (replay=0)。
     */
    private val eventMap = mutableMapOf<String, MutableSharedFlow<Any>>()

    /**
     * 存储粘性事件流的 Map。
     * Key: 事件的唯一标识符 (String)。
     * Value: 与该 Key 关联的 MutableSharedFlow (replay=1 或更大)。
     * 我们这里默认 replay=1，即只缓存最新的一个粘性事件。
     */
    private val stickyEventMap = mutableMapOf<String, MutableSharedFlow<Any>>()


    /**
     * 用于同步对 eventMap 和 stickyEventMap 访问的锁对象。
     */
    private val mapLock = Any()

    /**
     * 获取或创建指定 Key 的普通（非粘性）MutableSharedFlow。
     * @param key 事件的唯一标识符。
     * @return 对应 Key 的 MutableSharedFlow 实例 (replay=0)。
     */
    @PublishedApi
    internal fun getOrCreateFlow(key: String): MutableSharedFlow<Any> {
        return synchronized(mapLock) {
            eventMap.getOrPut(key) {
                MutableSharedFlow(replay = 0, extraBufferCapacity = 0)
            }
        }
    }

    /**
     * 获取或创建指定 Key 的粘性 MutableSharedFlow。
     * @param key 事件的唯一标识符。
     * @param replayCount 粘性事件的数量，默认为1（只缓存最新的一个）。
     * @return 对应 Key 的 MutableSharedFlow 实例 (replay > 0)。
     */
    @PublishedApi
    internal fun getOrCreateStickyFlow(key: String, replayCount: Int = 1): MutableSharedFlow<Any> {
        require(replayCount > 0) { "Replay count for sticky events must be greater than 0" }
        return synchronized(mapLock) {
            stickyEventMap.getOrPut(key) {
                MutableSharedFlow(
                    replay = replayCount,
                    extraBufferCapacity = 0
                )
            }
        }
    }

    // --- 普通事件 ---

    /**
     * 发送一个普通的、带有数据的事件。
     * @param T 事件数据的类型，必须是非空类型。
     * @param key 事件的唯一标识符。
     * @param value 要发送的事件数据。
     */
    fun <T : Any> postEvent(key: String, value: T) {
        eventBusScope.launch {
            getOrCreateFlow(key).emit(value)
        }
    }

    /**
     * 发送一个普通的不带数据的事件。
     * @param key 事件的唯一标识符。
     */
    fun postEvent(key: String) {
        eventBusScope.launch {
            getOrCreateFlow(key).emit(Unit)
        }
    }

    /**
     * 订阅指定 Key 的普通事件，返回一个 Flow。
     * @param T 期望接收的事件数据的类型，必须是非空类型。
     * @param key 事件的唯一标识符。
     * @return 一个 Flow<T>，它会发出与指定 Key 和类型匹配的事件。
     */
    inline fun <reified T : Any> getEventFlow(key: String): Flow<T> {
        return getOrCreateFlow(key)
            .asSharedFlow()
            .filterIsInstance<T>()
    }

    /**
     * 在指定的 LifecycleCoroutineScope 内订阅并观察普通事件。
     * @param T 期望接收的事件数据的类型，必须是非空类型。
     * @param lifecycleScope 与 Android 组件生命周期绑定的协程作用域。
     * @param key 事件的唯一标识符。
     * @param action 当接收到事件时执行的回调函数 (挂起函数)。
     */
    inline fun <reified T : Any> observeEvent(
        lifecycleScope: LifecycleCoroutineScope,
        key: String,
        crossinline action: suspend (T) -> Unit
    ) {
        getEventFlow<T>(key)
            .onEach { event -> action(event) }
            .launchIn(lifecycleScope)
    }

    /**
     * 在指定的 LifecycleCoroutineScope 内订阅并观察普通的不带数据的事件。
     */
    inline fun observeEvent(
        lifecycleScope: LifecycleCoroutineScope,
        key: String,
        crossinline action: suspend () -> Unit
    ) {
        getEventFlow<Unit>(key)
            .onEach { action() }
            .launchIn(lifecycleScope)
    }

    // --- 粘性事件 ---

    /**
     * 发送一个粘性的、带有数据的事件。
     * 新的订阅者会立即收到此事件（如果它是最新的）。
     * @param T 事件数据的类型，必须是非空类型。
     * @param key 事件的唯一标识符。
     * @param value 要发送的事件数据。
     * @param replayCount 发送时如果需要创建新的Flow，允许缓存的粘性事件数量，默认为1。
     *                    如果Flow已存在，则使用已存在Flow的replay设置。
     */
    fun <T : Any> postStickyEvent(key: String, value: T, replayCount: Int = 1) {
        eventBusScope.launch {
            // 注意：如果 getOrCreateStickyFlow 创建了新的 flow，它会使用 replayCount。
            // 如果 flow 已存在，则其原有的 replay 值不变。
            // 如果希望每次 post 都可能重置 replay 值，则需要更复杂的逻辑，
            // 通常一个key的粘性策略是固定的。
            getOrCreateStickyFlow(key, replayCount).emit(value)
        }
    }

    /**
     * 发送一个粘性的、不带数据的事件。
     * @param key 事件的唯一标识符。
     * @param replayCount 发送时如果需要创建新的Flow，允许缓存的粘性事件数量，默认为1。
     */
    fun postStickyEvent(key: String, replayCount: Int = 1) {
        eventBusScope.launch {
            getOrCreateStickyFlow(key, replayCount).emit(Unit)
        }
    }

    /**
     * 订阅指定 Key 的粘性事件，返回一个 Flow。
     * 新的订阅者会立即收到缓存的粘性事件（如果有）。
     * @param T 期望接收的事件数据的类型，必须是非空类型。
     * @param key 事件的唯一标识符。
     * @param replayCount 当第一次为此key创建Flow时，指定缓存数量，默认为1。
     *                    如果Flow已存在，此参数在获取时无效，将使用已存在Flow的replay设置。
     * @return 一个 Flow<T>，它会发出与指定 Key 和类型匹配的粘性事件。
     */
    inline fun <reified T : Any> onStickyEvent(key: String, replayCountWhenCreated: Int = 1): Flow<T> {
        return getOrCreateStickyFlow(key, replayCountWhenCreated)
            .asSharedFlow()
            .filterIsInstance<T>()
    }

    /**
     * 在指定的 LifecycleCoroutineScope 内订阅并观察粘性事件。
     * 新的订阅者会立即收到缓存的粘性事件（如果有）。
     * @param T 期望接收的事件数据的类型，必须是非空类型。
     * @param lifecycleScope 与 Android 组件生命周期绑定的协程作用域。
     * @param key 事件的唯一标识符。
     * @param replayCountWhenCreated 当第一次为此key创建Flow时，指定缓存数量，默认为1。
     * @param action 当接收到事件时执行的回调函数 (挂起函数)。
     */
    inline fun <reified T : Any> observeStickyEvent(
        lifecycleScope: LifecycleCoroutineScope,
        key: String,
        replayCountWhenCreated: Int = 1, // 当第一次为此key订阅并创建Flow时，指定缓存数量
        crossinline action: suspend (T) -> Unit
    ) {
        onStickyEvent<T>(key, replayCountWhenCreated)
            .onEach { event -> action(event) }
            .launchIn(lifecycleScope)
    }

    /**
     * 在指定的 LifecycleCoroutineScope 内订阅并观察粘性的不带数据的事件。
     */
    inline fun observeStickyEvent(
        lifecycleScope: LifecycleCoroutineScope,
        key: String,
        replayCountWhenCreated: Int = 1,
        crossinline action: suspend () -> Unit
    ) {
        onStickyEvent<Unit>(key, replayCountWhenCreated)
            .onEach { action() }
            .launchIn(lifecycleScope)
    }


    // --- 清理方法 ---

    /**
     * 清理并移除指定 Key 的普通事件流。
     * @param key 要清理的事件的唯一标识符。
     */
    fun clearEvent(key: String) {
        synchronized(mapLock) {
            eventMap.remove(key)?.resetReplayCache() // For replay=0, resetReplayCache does nothing.
        }
    }

    /**
     * 清理并移除指定 Key 的粘性事件流。
     * @param key 要清理的粘性事件的唯一标识符。
     */
    fun clearStickyEvent(key: String) {
        synchronized(mapLock) {
            stickyEventMap.remove(key)?.resetReplayCache() // Clears cached sticky events.
        }
    }

    /**
     * 清理并移除所有普通和粘性事件流。
     */
    fun clearAll() {
        synchronized(mapLock) {
            eventMap.values.forEach { it.resetReplayCache() }
            eventMap.clear()
            stickyEventMap.values.forEach { it.resetReplayCache() }
            stickyEventMap.clear()
        }
    }

 }